package com.lee.pullrefresh.http;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONException;

import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;

public class HttpTaskRunner implements Runnable {

	//protected Context mContext;
	public HttpTaskRunner() {
		setRequestProperty("connection", "Keep-Alive");
		setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
		setRequestProperty("accept", "*/*");
	}

	final public static String HTTP_GET = "GET";
	final public static String HTTP_POST = "POST";
	protected String encode = "UTF-8";
	protected String recv_encode = "UTF-8";
	
	public String mUrl;
	
	/*
	 * define http request method
	 */
	public String mMethod = HTTP_POST;
	
	protected boolean mTaskIsRunning = false;
	public boolean isRunning() {return mTaskIsRunning;}
	

	/*
	 * get protocol parameter
	 */
	
	protected ArrayList<BasicNameValuePair> mUrlSubjoin;
	
	public void putUrlSubjoins() {}
	
	public void clearUrlSubjion() {
		if (mUrlSubjoin != null)
			mUrlSubjoin.clear();
	}	
	public String urlSubjoin() {
		clearUrlSubjion();
		putUrlSubjoins();
		String ret = null;
		try {
			UrlEncodedFormEntity afxMsg = new UrlEncodedFormEntity(mUrlSubjoin,encode);
			ret = stream2String(afxMsg.getContent());
		} catch (Exception e) {
		}
		return ret;
	}

	public void putUrlSubjoin(String key, double value) {
		putUrlSubjoin(key, String.valueOf(value));
	}
	public void putUrlSubjoin(String key, long value) {
		putUrlSubjoin(key, String.valueOf(value));
	}
	public void putUrlSubjoin(String key, int value) {
		putUrlSubjoin(key, String.valueOf(value));
	}
	public void putUrlSubjoin(String key, String value) {
		if (TextUtils.isEmpty(key) || null == value) {
			return;
		}
		if (null == mUrlSubjoin)
			mUrlSubjoin = new ArrayList<BasicNameValuePair>();
		mUrlSubjoin.add(new BasicNameValuePair(key, value));
	}

	
	/*
	 * post protocol parameter
	 */
	protected ArrayList<BasicNameValuePair> mPostBodyes;
	
	public void putBodys() {}
	
	public void clearBody() {
		if (mPostBodyes != null)
			mPostBodyes.clear();
	}
	public void putPostBody(String key, long value) {
		putPostBody(key, String.valueOf(value));
	}
	public void putPostBody(String key, int value) {
		putPostBody(key, String.valueOf(value));
	}
	public void putPostBody(String key, String value) {
		if (TextUtils.isEmpty(key) || null == value) {
			return;
		}
		if (null == mPostBodyes)
			mPostBodyes = new ArrayList<BasicNameValuePair>();
		mPostBodyes.add(new BasicNameValuePair(key, value));
	}

	private String encodedBody() {
		clearBody();
		putBodys();
		String ret = null;
		try {
			UrlEncodedFormEntity afxMsg = new UrlEncodedFormEntity(mPostBodyes,encode);
			//ret = URLDecoder.decode(stream2String(afxMsg.getContent()));
			ret = stream2String(afxMsg.getContent());
		} catch (Exception e) {
		}
		return ret;
	}

	private long getPostBodyLength() {
		long ret = 0;
		try {
			UrlEncodedFormEntity afxMsg = new UrlEncodedFormEntity(mPostBodyes,encode);
			ret = afxMsg.getContentLength();
		} catch (Exception e) {
		}
		return ret;
	}

	//set request property 
	static protected Map<String, String> mRequestPropertys; 
	static public void setRequestProperty(String field, String newValue) {
		if (null == mRequestPropertys) {
			mRequestPropertys = new HashMap<String, String>();
		}	
		mRequestPropertys.put(field, newValue);
	}
	
	static public void clearRequestProperty() {
		if (null != mRequestPropertys) {
			mRequestPropertys.clear();
		}
		mRequestPropertys = null;
	}

	
	/*
	 * task event listener
	 */
	public static final int TID_MSG = 0x1;
	public static final int TID_TIMEOUT = 0x2;
	public static final int TIME_OUT_DEALY = 1000 * 30;

	public interface OnEventListener {
		public void onFinished(Message msg);
	}

	protected OnEventListener mListener = null;

	public void setOnEventListener(OnEventListener listener) {
		mListener = listener;
	}

	protected Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (mListener != null) {
				mListener.onFinished(msg);
			}
		}
	};

	/*
	 * InterruptObject class
	 */
	public class InterruptObject {
		public volatile boolean mInterrupted = false;

		private boolean isInterrupted() {
			return mInterrupted || Thread.interrupted();
		}

		public void peekInterruptStatus() throws InterruptedException {
			if (isInterrupted())
				throw new InterruptedException("User was cancelled the thread");
		}

		public void interrupt() {
			mInterrupted = true;
		}

		public void reset() {
			mInterrupted = false;
		}
	}

	private InterruptObject mInterruptObject = new InterruptObject();

	public void reset() {
		mErrorInfo = null;
		mInterruptObject.reset();
	}


	public void cancel() {
		mInterruptObject.interrupt();
	}

	public void execute() throws Exception {
		if (TextUtils.isEmpty(mUrl)) {
			throw new IllegalArgumentException("invalid url");
		}
		mTaskIsRunning = true;
		HttpTaskExecutor.getInstance().execute(this);
	}

	private void feedbackMsg(int what, int arg1, int arg2) {
		if (mHandler != null) {
			Message message = this.mHandler.obtainMessage();
			message.what = what;
			message.obj = this;
			message.arg1 = arg1;
			message.arg2 = arg2;
			mHandler.sendMessage(message);
		}
		mTaskIsRunning = false;
	}

	final public static int CODE_OK 		= 0;
	final public static int CODE_NORMAL		= -1;
	//final public static int RCODE_AWAIT 		= 0x1;
	final public static int CODE_USER_CANCEL 	= 0x2;
	protected int mOpCode = CODE_NORMAL;
	
	/*
	public int getOpCode() {
		return mOpCode;
	}
	
	public int setAwait() {
		return mOpCode = RCODE_AWAIT;
	}
	public boolean isAwait() {
		return RCODE_AWAIT == mOpCode;
	}
	*/
	
	

	
	public static final int CONNECTION_TIME_OUT = 10000;
	public static final int READ_TIME_OUT = 15000;
	
	private String makeRequestUrl() {
		StringBuffer sbUrl = new StringBuffer();
		sbUrl.append(mUrl);
		String tmp = urlSubjoin();
		if (!TextUtils.isEmpty(tmp)) {
			Log.d("TAG", "HttpTaskRunner.Task->run->urlSubjoin :" + tmp);
			if (sbUrl.toString().endsWith("?")) {
				sbUrl.append(tmp);
			} else {
				sbUrl.append("?").append(tmp);
			}
			sbUrl.append("&ts=");			//��ֹ���ػ������ݣ��˲�������˲�����
			sbUrl.append(System.currentTimeMillis());
		}
		return sbUrl.toString();
	}

	@Override
	public void run() {
		reset();
		mOpCode = CODE_NORMAL;
		InputStream responseStream = null;
		String responseContent = null;
		String requestUrl = makeRequestUrl();
		Log.d("TAG", "HttpTaskRunner.Task->run->the url is =>" + requestUrl);
		HttpURLConnection httpConnecter = null;
		int retryTime = 4;
		while (mOpCode != CODE_OK  && retryTime-- > 0) {
			httpConnecter = NetUtils.getConncetion(requestUrl);
			if (httpConnecter != null) {
				try {
					responseContent = null;
					for (Map.Entry entry : mRequestPropertys.entrySet()) {     
					    String field = (String)entry.getKey();
					    String newValue = (String)entry.getValue();
					    httpConnecter.setRequestProperty(field, newValue);
					}  
					httpConnecter.setConnectTimeout(CONNECTION_TIME_OUT);
					httpConnecter.setUseCaches(false);
					httpConnecter.setDoInput(true);
					httpConnecter.setReadTimeout(READ_TIME_OUT);
					httpConnecter.setInstanceFollowRedirects(true);

					if (mMethod.equals(HTTP_POST)) {
						String body = encodedBody();
						if (!TextUtils.isEmpty(body)) {
							Log.d("TAG", body);
							httpConnecter.setDoOutput(true);
							httpConnecter.setRequestMethod(mMethod);
							/*
							 * application/x-www-form-urlencoded���������ݱ�����Ϊ����/ֵ�ԡ����Ǳ�׼�ı����ʽ
							 * ������Ĭ�ϵķ�ʽ
							 * multipart/form-data���������ݱ�����Ϊһ����Ϣ��ҳ�ϵ�ÿ���ؼ���Ӧ��Ϣ�е�һ������
							 * �����������ݴ��䷽ʽ����Ҫ�����ϴ��ļ�
							 * text/plain�����������Դ��ı���ʽ���б��룬���в����κοؼ����ʽ�ַ���
							 */
							//con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");							
							byte[] bytes = body.getBytes(recv_encode);
							httpConnecter.setRequestProperty("Content-Length", Integer.toString(bytes.length));
							DataOutputStream dos = new DataOutputStream(httpConnecter.getOutputStream());
							dos.write(bytes);
							dos.flush();
							dos.close();
						}
					}
					mInterruptObject.peekInterruptStatus();
					int responseCode = httpConnecter.getResponseCode();
					Log.d("TAG", "DefaultClient->httpRequest->responseCode is ->"+ responseCode);
					mInterruptObject.peekInterruptStatus();
					if (responseCode >= HttpURLConnection.HTTP_OK && responseCode <= HttpURLConnection.HTTP_PARTIAL) {
						responseStream = httpConnecter.getInputStream();
						if (responseStream != null) {
							responseContent = stream2String(responseStream);
							mOpCode = CODE_OK;
						} else {
							mOpCode = -3;
						}
					} else if (HttpURLConnection.HTTP_MOVED_TEMP == responseCode || HttpURLConnection.HTTP_MOVED_PERM == responseCode) {
						String redirectUrl = httpConnecter.getHeaderField("Location");
						if (!TextUtils.isEmpty(redirectUrl)) {
							mUrl = redirectUrl;
							requestUrl = makeRequestUrl();
						}
					} else {
						mOpCode = responseCode; //-500
					}
				} catch (NullPointerException ex) {
					Log.d("TAG", "HttpTaskRunner.NullPointerException->" + ex.toString());
					mOpCode = -20;
				} catch (InterruptedException ex) {
					Log.d("TAG", "HttpTaskRunner.InterruptedException->" + ex.toString());
					mOpCode = CODE_USER_CANCEL;
				} catch (MalformedURLException ex) {
					Log.e("TAG", "HttpTaskRunner.MalformedURLException;Invalid url format->", ex);
				} catch (ProtocolException ex) {
					Log.e("TAG", "HttpTaskRunner.ProtocolException;Un support protocol->", ex);
				} catch (IOException ex) {
					Log.d("TAG", "HttpTaskRunner.IOException->" + ex.toString());
					//IOException->java.net.SocketTimeoutException: failed to connect to /10.28.175.246 (port 80) after 30000ms
					mOpCode = -40;
				} catch (Exception e) {
					Log.d("TAG", "Exception->" + e.toString());
					mOpCode = -50;
				} finally {
					if (responseStream != null) {
						try {
							responseStream.close();
						} catch (IOException e) {
						}
						responseStream = null;
					}
					if (httpConnecter != null) {
						httpConnecter.disconnect();
						httpConnecter = null;
					}
				}
			} else {
				mOpCode = -2;
			}			
		}		
		Log.d("TAG","http task request error code is -> " + mOpCode);
		if (CODE_OK == mOpCode) { 
			if (!TextUtils.isEmpty(responseContent)) {
				Log.d("TAG",responseContent);
				try {
					parse(responseContent);
				} catch (JSONException e) {
					mOpCode = -1000;
					networkError(mOpCode);
				}
			} else {
				mErrorInfo = "����˷����쳣�����Ժ�����";
			}
		} else {
			networkError(mOpCode);
		}
		
		try {
			mInterruptObject.peekInterruptStatus();
		} catch (InterruptedException e) {
			mTaskIsRunning = false;
			return;
		}
		
		feedbackMsg(TID_MSG, mOpCode, 0);
	}

	public String mErrorInfo;
	public void networkError(int code) {
		mErrorInfo = "�����������쳣����ȷ����������";
	}

	private String stream2String(InputStream stream)
			throws IOException, NullPointerException {
		String responseAsString = null;
		BufferedReader br;
		if (null == stream) {
			Log.d("TAG", "ResponseFirst->asString()->null == stream->");
			throw new NullPointerException();
		}
		br = new BufferedReader(new InputStreamReader(stream, recv_encode));
		StringBuffer buf = new StringBuffer();
		String line = br.readLine();
		while (null != line) {
			buf.append(line);
			line = br.readLine();
			if (line != null) {
				buf.append("\n");
			}
		}
		responseAsString = buf.toString();
		return responseAsString;
	}

	public void parse(String responseContent) throws JSONException {}
}
